

// 测试监听事件 _listensock 的作用   
// 一个 telnet 127.0.0.1 8081 来连接，看看 _listensock accept 新 sock 的现象 


#pragma once

#include <cstring>
#include <iostream>
#include <string>
#include <functional>
#include "sock.hpp"

namespace select_ns{
    static const int defaultport = 8081;

    class SelectServer{
    public:
        SelectServer(int port = defaultport) : _port(defaultport)
        {}

        void initServer(){
            _listensock = Sock::Socket();
            Sock::Bind(_listensock, _port);
            Sock::Listen(_listensock);
            logMessage(NORMAL, "create socket success: %d", _listensock);
        }

        void HandlerEvent(fd_set& rfds){
            // 调用 HandlerEvent 函数之前是会调用 select 函数的 ---> select 函数告诉我们，有事件就绪了，但是不知道是哪一个事件就绪了。
            // 但是我们这份代码只有一个 _listensock 被设置进 rfds 了，所以也必定是 _listensock(监听事件) 就绪了。
            // 监听事件就绪了 ---> 监听事件是获取新的 sock 连接的。
            // 后面版本的代码 rfds 设置多个 fd 的时候，每一个 fd 都是做不一样的事件的。
            // _listensock fd 是负责监听新的 sock 的（给它设置一个Accept函数）。后面的代码中其它的 fd 一般是有其它的任务的得先读取上来（给他们设置Recver函数）
            // 现在的这个版本的代码，下面外层的 for 循环就不用了写上了

            // for(int i = 0; i < fd_num; i++){
            //     if(fdarray[i] == defaultfd) continue;   // 排除非法的 fd

                // 先判断 _listensock 是否在 rfds 里面
                if(FD_ISSET(_listensock, &rfds)){ // 监听事件 fd 就绪了
                    // 走到这里的时候，下面的acccept是不会阻塞的
                    // select 告诉我 _listensock 读事件就绪了
                    std::string clientip;
                    uint16_t clientport = 0;
                    // 新的 sock 拿上来后，队列里面就没有了 ---> 新连接从 telnet 断开后就不会一直打印 have event ready! 了，因为新 sock 被拿走了
                    int sock = Sock::Accept(_listensock, &clientip, &clientport);
                    if(sock < 0) return ;
                    logMessage(NORMAL, "accept success [%s : %d]", clientip.c_str(), clientport);

                    // sock 我们能直接 recv / read 吗？ 不能，整个代码，只有select有资格检测事件是否就绪
                    // 将新的sock托管给select !
                }

        //         else{   // 处理任务事件 fd 就绪了

        //         }
        //     }
        // }

        void start(){
            fd_set rfds;
            FD_ZERO(&rfds);
            FD_SET(_listensock, &rfds);            

            for(;;){
                // 下面的情况式：不设置 timavalue 的话，默认是阻塞式IO，知道有就绪的socket才select
                // int n = select(_listensock + 1, &rfds, nullptr, nullptr, nullptr);

                // 下面的情况式：先阻塞式3秒，再select
                // struct timeval timevalue = {3, 0};
                // int n = select(_listensock + 1, &rfds, nullptr, nullptr, &timevalue);

                struct timeval timevalue = {3, 0};
                int n = select(_listensock + 1, &rfds, nullptr, nullptr, &timevalue);
                switch(n)
                {
                case 0:
                    logMessage(NORMAL, "timeout...");
                    break;
                case -1:
                    logMessage(WARNING, "select error, code: %d, err: string: %s", errno, strerror(errno));
                    break;
                default:
                    logMessage(NORMAL, "have event ready!");
                    sleep(1);    // 模拟事件处理
                    // HandlerEvent(rfds);
                    break;
                }

                // 阻塞式 IO
                // std::string clientip;
                // uint16_t clientport = 0;
                // int sock = Sock::Accept(_listensock, &clientip, &clientport);
                // if(sock < 0) continue;
                // 开始进行服务器的处理逻辑
            }
        }

        ~SelectServer(){
            if(_listensock < 0) close(_listensock);
        }
    private:
        int _port;
        int _listensock;
    };
}



